package gameplay;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

public class PathGroup {
  private ArrayList<ArrayList<Point>> listOfPath =
      new ArrayList<ArrayList<Point>>();
  private static int testCount = 0;
  private HashMap<String, Boolean> visitedPoints = new HashMap<String, Boolean>();

  

  public PathGroup() {
    
  }
  
  public PathGroup(PathGroup pathGroup) {
    for (int i = 0; i < pathGroup.getListOfPath().size(); i++) {
      this.listOfPath.add(new ArrayList<Point>());
      for (int j = 0; j < pathGroup.getListOfPath().get(i).size(); j++) {
      this.listOfPath.get(i).add(pathGroup.getListOfPath().get(i).get(j));
      }
    }

    for (Entry<String, Boolean> point : pathGroup.getVisitedPoints().entrySet()) {
      this.visitedPoints.put(point.getKey(), point.getValue());
    }
  }
  
  
  public void sortPaths() {
    Collections.sort(listOfPath, new Comparator<ArrayList<Point>>() {
        public int compare(ArrayList<Point> a, ArrayList<Point> b) {
           return Integer.signum(b.size() - a.size());
        }
    });
  }
  
  public String toString() {
    Iterator<ArrayList<Point>> iteratorTotal = listOfPath.iterator();
    String toPrint = "";
    toPrint += "{ ";
    while (iteratorTotal.hasNext()) {
      toPrint += "[";
      Iterator<Point> iterator = iteratorTotal.next().iterator();
        while (iterator.hasNext()) {
          toPrint += iterator.next() + ",";
        }
        toPrint += "], ";
    }
    toPrint += "} ";
  return toPrint;    
  }


  public void setListOfPath(ArrayList<ArrayList<Point>> listOfPath) {
    this.listOfPath = listOfPath;
  }
  
  public void addVisitedPoint(Point point) {
    visitedPoints.put(point.toString(), true);  
  }
  
  public Boolean isPointVisited(Point point) {
    return (visitedPoints.get(point.toString()) != null);
  }
  
  public ArrayList<ArrayList<Point>> getListOfPath() {
    return this.listOfPath;
  }
  
  public void addToList(ArrayList<Point> pointList) {
    this.listOfPath.add(pointList);
  }
  
  public void addToList(int index, Point point) {
    this.listOfPath.get(index).add(point);
  }
  
  public void addScoreList(ArrayList<Point> pointList) {
    this.listOfPath.add(pointList);
  }
  
  public int getListOfPathSize() {
    return listOfPath.size();
  }
  
  public ArrayList<Point> getListOfPath(int index) {
    return listOfPath.get(index);
  }  
  
  public HashMap<String, Boolean> getVisitedPoints() {
    return visitedPoints;
  }

  public void setVisitedPoints(HashMap<String, Boolean> visitedPoints) {
    this.visitedPoints = visitedPoints;
  }
  
 
  public int getNumberOfPoint () {
    Iterator<ArrayList<Point>> iterator = listOfPath.iterator();
    int numbreOfPoint = 0;
    while (iterator.hasNext()) {
      numbreOfPoint += iterator.next().size();
    }
    return numbreOfPoint;
    
  }
  
  
  
  
  public int getTotalScore() {
    Iterator<ArrayList<Point>> iterator = listOfPath.iterator();
      int score = 0;
      while (iterator.hasNext()) {
        score += getListofPathScore(iterator.next());
      }
      return score;
    }

  
  
  
  /**
   * 
   * @param pointList
   * @return
   */
  private static int getListofPathScore(ArrayList<Point> pointList) {
    int sizeList = pointList.size();
    if (sizeList == 1 || sizeList == 0) {
      return 0;
    }
    int score = 0;
    int precedentChain = 0;
    int currentChain = 1; 
    boolean scoreGrow,  
        isGrow = pointList.get(1).getValue() > pointList.get(0).getValue(),
        currentChainIncrement;
    boolean anyDirection = true;
    int precedentValue;
    for (int i = 1; i < sizeList; i++) {
        if (pointList.get(i).getValue() != pointList.get(i - 1).getValue()
            && pointList.get(i).getValue() != 31 &&
            !(pointList.get(0).getValue() == 31 && i == 1)) {
          precedentValue = pointList.get(i - 1).getValue() == 31 ?
              pointList.get(i - 2).getValue() : pointList.get(i - 1).getValue();

          scoreGrow = (pointList.get(i).getValue() > precedentValue) == isGrow 
              || pointList.get(i).getValue() == precedentValue || anyDirection;
          if (!scoreGrow) {
            currentChainIncrement = false;
            if (precedentChain != 0) {
              if (precedentChain <= currentChain) {
              precedentChain--;
              } else {
                currentChainIncrement = true;  
              }
            }
            score += getScoreFromChain(precedentChain); 
            precedentChain = currentChain; 
            currentChain = currentChainIncrement ? 2 : 1;
          } else {
            currentChain++;
          }
          if (pointList.get(i).getValue() != precedentValue) {
            isGrow = pointList.get(i).getValue() > precedentValue;
            anyDirection = false;
          }
        } else {
          currentChain++;
          if (pointList.get(i).getValue() == 31 && currentChain == 2) {
            anyDirection = true;
            score += getScoreFromChain(precedentChain);
            precedentChain = 1;
          }
        }
    }
    if (precedentChain > 1 &&  precedentChain <= currentChain) {
      precedentChain--;
      currentChain++;
    }
    score += getScoreFromChain(precedentChain);
    score += getScoreFromChain(currentChain);
    return score;
  }
    
  
  private static int getScoreFromChain(int chain) {
    return Grid.scores[chain >= Grid.scores.length ? Grid.scores.length -1 : chain];
  }
    
    
    
  public boolean testGetPathListScore() {
    boolean testResult = true;
    Point[] p = new Point[32];
    for (int i = 0; i < p.length; i++) {
      p[i] = new Point(i); 
    }
    testCount = 0;
    

    
    testResult = testCase(Arrays.asList(p[2], p[3], p[4], p[2], p[3], p[4], p[2], p[3], p[5], p[6], p[7]), (3 + 3 + 7)) && testResult;  
    testResult = testCase(Arrays.asList(p[9], p[8]), (1)) && testResult;
    testResult = testCase(Arrays.asList(p[2], p[4], p[5], p[5], p[3], p[8], p[2], p[3], p[5], p[6], p[7]), (5 + 1 + 7)) && testResult;
    testResult = testCase(Arrays.asList(p[10], p[9], p[5], p[4], p[3], p[4], p[2], p[1]), (7 + 3)) && testResult;
    testResult = testCase(Arrays.asList(p[10], p[9], p[5], p[4], p[3], p[2], p[1]), (11)) && testResult;
    testResult = testCase(Arrays.asList(p[10], p[9], p[5], p[6], p[7], p[8], p[9]), (1 + 7)) && testResult;
    testResult = testCase(Arrays.asList(p[1], p[5], p[5], p[6], p[7]), (7)) && testResult;
    testResult = testCase(Arrays.asList(p[9], p[5], p[6], p[7], p[8], p[9], p[10]), (9)) && testResult;
    testResult = testCase(Arrays.asList(p[10], p[9], p[5], p[4], p[3], p[9], p[5], p[6], p[7], p[8], p[9], p[10]), (7 + 0 + 9)) && testResult;
    testResult = testCase(Arrays.asList(p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[14],p[15],p[9],p[8],p[7],p[8],p[9],p[10],p[8]), (50 + 1 + 5)) && testResult;
    testResult = testCase(Arrays.asList(p[7], p[4], p[4], p[4], p[3], p[4], p[2], p[3], p[5], p[6], p[7]), (7 + 0 + 7)) && testResult;
    testResult = testCase(Arrays.asList(p[1],p[2],p[31],p[4],p[5]), (7)) && testResult;
    testResult = testCase(Arrays.asList(p[1],p[2],p[31],p[8],p[12]), (7)) && testResult;
    testResult = testCase(Arrays.asList(p[1],p[2],p[31],p[8],p[5]), (5 + 0)) && testResult;
    testResult = testCase(Arrays.asList(p[1],p[2],p[31],p[8],p[5],p[2]), (1 + 5)) && testResult;
    testResult = testCase(Arrays.asList(p[1],p[2],p[8],p[31],p[2],p[3],p[4]), (5 + 3)) && testResult;
    testResult = testCase(Arrays.asList(p[31],p[12],p[24],p[1],p[2],p[5]), (3 + 3)) && testResult;
    testResult = testCase(Arrays.asList(p[31],p[12],p[24],p[1],p[2],p[5]), (3 + 3)) && testResult;
    testResult = testCase(Arrays.asList(p[31],p[12],p[24],p[1],p[2],p[5]), (3 + 3)) && testResult;
    testResult = testCase(Arrays.asList(p[18],p[19],p[27],p[04],p[31],p[10], p[12], p[14], p[14], p[16], p[17], p[17], p[23], p[24], p[25], p[28], p[9], p[11], p[13],p[26]) , (3 + 40 + 5)) && testResult;
    testResult = testCase(Arrays.asList(p[19],p[8],p[17],p[21],p[3],p[2], p[5], p[11], p[11], p[13], p[14], p[15], p[16], p[17], p[18], p[18], p[24], p[26], p[22],p[30], p[13]) , (3 + 40 + 1)) && testResult;
    //testResult = testCase(Arrays.asList(p[18],p[19],p[27],p[19],p[04],p[31],p[10], p[12], p[14], p[14], p[16], p[17], p[17], p[23], p[24], p[25], p[28], p[9], p[11], p[13],p[26]) , (3 + 40 + 5)) && testResult;
    return testResult;
  }

  private static int getListofPathScore2(ArrayList<Point> pointList) {
    int sizeList = pointList.size();
    if (sizeList == 1 || sizeList == 0) {
      return 0;
    }
    int score = 0;
    int precedentChain = 0;
    int currentChain = 1; 
    
    boolean isGrow = pointList.get(1) .getValue() > pointList.get(0).getValue(),
            isPrecedentGrow = true,
            scoreGrow = true,
            isJunction = false;
    boolean anyDirection = true;
    int precedentValue = 0,
        commonPoint = 0,
        precedentCommonPoint = 0,
        currentChainIncrement = 0;
    
    if (testCount == 8) {
      testCount --;testCount ++;
    }
    for (int i = 1; i < sizeList; i++) { 

        precedentValue = pointList.get(i - 1).getValue();
        
        if (pointList.get(i).getValue() == 31 || 
            pointList.get(i).getValue() == precedentValue) {
          commonPoint++;
          anyDirection = true;
        } else {
          isGrow = pointList.get(i) .getValue() > precedentValue;
        }
        
        scoreGrow = isGrow == isPrecedentGrow || anyDirection; 
        anyDirection = false;
        if (!scoreGrow) { // New List
          currentChainIncrement = 0;
          if (precedentChain < currentChain) {
            precedentChain = Math.max(precedentChain - 1, 0);
          } else {
            currentChainIncrement = 1; 
          }
          
          score += Grid.scores[precedentChain];
          precedentChain = currentChain;
          currentChain = 1 + currentChainIncrement;
        } else { 
          currentChain++;
          commonPoint = 0;
        }
        isPrecedentGrow = isGrow;
    }

    if (precedentChain < currentChain) {
      precedentChain = Math.max(precedentChain - precedentCommonPoint, 0);
      currentChain += precedentCommonPoint;
    }
    score += Grid.scores[precedentChain]; 
    score += Grid.scores[currentChain];
    return score;
  }
  
  
  
  
//                         0  1  2  3  4  5  6  7   8   9   10  11  12  13  14  15  16  17   18  19   20
//  static int[] scores = {0, 0, 1, 3, 5, 7, 9, 11, 15, 20, 25, 30, 35, 40, 50, 60, 70, 85, 100, 150, 300};
  
   public boolean testCase(List<Point> list, int expectedScore) {
     testCount++;
     ArrayList<Point> scoreList = new ArrayList<Point>(list);
     int score = getListofPathScore(scoreList);
     if (score != (expectedScore)) { 
       System.out.println("This path(" + testCount + ") : " + list + " have a score : " + score +
           " and should have the score " + expectedScore);
       return false;
     } else {
       System.out.println("Test " + testCount + " Passed.");
       return true;
     }
      
   }
}
